#include "Server.h"
#include <QCoreApplication>
#include <QFile>
#include <QDir>
#include <QTimer>
#include <QJsonDocument>
#include <QDebug>
#include <unistd.h>
#include <csignal>
#include <signal.h>

Server::Server(QObject *parent) : QObject(parent), server(nullptr), currentSocket(nullptr), pFunc(nullptr), pidA(0) {
    // Initialize Python when the Server object is created
}

Server::~Server() {
    if (server) {
        server->close();
        delete server;
    }
    Py_Finalize();
}

uint64_t Server::getCurrentMillis() {
    auto now = std::chrono::system_clock::now();
    auto duration_since_epoch = now.time_since_epoch();
    return std::chrono::duration_cast<std::chrono::milliseconds>(duration_since_epoch).count();
}

void Server::handlePythonResult(PyObject *pResult) {
    const char *jsonCStr = PyUnicode_AsUTF8(pResult);
    if (!jsonCStr) {
        qDebug() << "Failed to convert Python JSON string to C string!";
        Py_DECREF(pResult);
        return;
    }

    QByteArray jsonByteArray = QByteArray(jsonCStr);
    QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonByteArray);
    if (jsonDoc.isNull()) {
        qDebug() << "Failed to parse JSON data!";
        Py_DECREF(pResult);
        return;
    }

    QString response = jsonDoc.toJson(QJsonDocument::Compact) + "<END>";
    qDebug()<<"啊大数据开发好看的撒发货"<<response;
    Py_DECREF(pResult);
    currentSocket->write(response.toUtf8());
    currentSocket->flush();
}

void Server::checkProcessA() {
    if (kill(pidA, 0) != 0) {
        qDebug()<< "Process " << pidA << " has terminated. Exiting..." ;
        QCoreApplication::quit();
    }
}

void Server::processImageData(const QByteArray &imageData,bool isTest) {
    PyObject *pImageBytes = PyBytes_FromStringAndSize(imageData.constData(), imageData.size());
    PyObject *pBool = PyBool_FromLong(isTest);
    PyObject *pArgs = PyTuple_Pack(2, pImageBytes, pBool);

    if (!pArgs) {
        qDebug()<< "Failed to pack arguments for Python function" ;
        Py_DECREF(pImageBytes);
        Py_DECREF(pBool);
        return;
    }

    PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
    Py_DECREF(pImageBytes);
    Py_DECREF(pBool);
    Py_DECREF(pArgs);

    handlePythonResult(pResult);

    const_cast<QByteArray &>(imageData).clear();


//    QString response = "{\"data\":{\"cls\":[39,0,0],\"track_ids\":[1,2,3],\"xyxys\":[[79.73339080810547,112.6861572265625,103.33824920654297,186.19052124023438],[109.09941101074219,200.029296875,179.93370056152344,319.673828125],[53.555450439453125,161.5467529296875,180,319.35821533203125]]}}<END>";
//    currentSocket->write(response.toUtf8());
//    currentSocket->flush();
}

void Server::handleNewConnection() {
    qDebug()<< "Registered handleNewConnection" ;
    currentSocket = server->nextPendingConnection();
    if (!currentSocket)
        return;

    QObject::connect(currentSocket, &QLocalSocket::readyRead, [this]() {
        qDebug()<< "Receiving data: " << getCurrentMillis() ;
        static qint64 imageSize = -1;
        static QByteArray imageData;
        static bool flag = false;
        while (currentSocket->bytesAvailable() > 0) {
            if (imageSize == -1) {
                if (currentSocket->bytesAvailable() < sizeof(flag)) return;
                currentSocket->read(reinterpret_cast<char *>(&flag), sizeof(flag));
                qDebug() << "Received flag:" << flag;

                if (currentSocket->bytesAvailable() < sizeof(imageSize))
                    return;

                currentSocket->read(reinterpret_cast<char *>(&imageSize), sizeof(imageSize));
                imageData.clear();
                qDebug()<< "Image size to receive: " << imageSize ;
            }

            qint64 bytesToRead = std::min(imageSize - imageData.size(), currentSocket->bytesAvailable());
            imageData.append(currentSocket->read(bytesToRead));

            if (imageData.size() == imageSize) {
                qDebug()<< "Image received successfully, size: " << imageSize ;
                processImageData(imageData, flag);
                imageSize = -1;
            }
        }
    });
}

void Server::initializePython(const std::string &pythonPath) {
    Py_Initialize();
    if (!Py_IsInitialized()) {
        qDebug()<< "Failed to initialize Python" ;
        exit(-1);
    }

    signal(SIGINT, SIG_DFL);

    std::string pythonCode = "import sys\nsys.path.append('" + pythonPath + "')\n";
    PyRun_SimpleString(pythonCode.c_str());

    PyObject *pModule = PyImport_ImportModule("track");
    if (!pModule) {
        PyErr_Print();
        qDebug()<< "Failed to load track module" ;
        exit(-1);
    }

    pFunc = PyObject_GetAttrString(pModule, "process_touch");
    if (!pFunc || !PyCallable_Check(pFunc)) {
        PyErr_Print();
        qDebug()<< "Cannot find function process_touch" ;
        Py_DECREF(pModule);
        exit(-1);
    }

    PyObject *pDirFunc = PyObject_GetAttrString(pModule, "current_directory");
    if (pDirFunc && PyCallable_Check(pDirFunc)) {
        PyObject *pDirResult = PyObject_CallObject(pDirFunc, nullptr);
        if (pDirResult) {
            const char *dir = PyUnicode_AsUTF8(pDirResult);
            qDebug() << "Current directory: " << dir ;
            Py_DECREF(pDirResult);
        } else {
            PyErr_Print();
            qDebug()<< "Failed to call current_directory" ;
        }
        Py_DECREF(pDirFunc);
    } else {
        PyErr_Print();
        qDebug()<< "Cannot find function current_directory" ;
        Py_XDECREF(pDirFunc);
    }
}

void Server::startServer(const QString &processId) {
    QString socketPath = QDir::temp().absoluteFilePath(processId);
    if (QFile::exists(socketPath)) {
        qDebug() << "Delete old socket files" ;
        QFile::remove(socketPath);
    }

    server = new QLocalServer();
    server->listen(processId);
    QObject::connect(server, &QLocalServer::newConnection, this, &Server::handleNewConnection);
    if (!server->isListening()) {
        qDebug()<< "Unable to start the server: " << server->errorString() ;
        exit(-1);
    }

    qDebug() << "Server started successfully!" << getCurrentMillis() ;
}
